#include "trans.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct train_s{
    int type;
    int length;
    char value[1024];
}train_t;
int recvFileFromServer(int sockfd) {
    
    train_t train;
    recv(sockfd, &train.type, sizeof(train.type), MSG_WAITALL);
    if(train.type == -1)
    {
        return -1;
    }
    memset(&train, 0, sizeof(train));
    ssize_t ret = recv(sockfd, &train.length, sizeof(train.length), MSG_WAITALL);
    if (ret <= 0) {
        printf("接收文件名长度失败");
        return -1;
    }
    ret = recv(sockfd, train.value, train.length, MSG_WAITALL);
    if (ret <= 0) {
        printf("接收文件名失败");
        return -1;
    }
    train.value[train.length] = '\0'; // 确保字符串终止

    int file_fd = open(train.value, O_RDWR | O_CREAT, 0666);
    if (file_fd == -1) {
        printf("打开文件失败");
        return -1;
    }

    char size_file_name[64] = {0};
    strncpy(size_file_name, train.value, sizeof(size_file_name) - 6); // 留空间给 ".size"
    strcat(size_file_name, ".size");
    int file_size_fb = open(size_file_name, O_RDWR | O_CREAT, 0666);
    if (file_size_fb == -1) {
        printf("打开大小文件失败");
        close(file_fd);
        return -1;
    }

    ssize_t client_file_size = 0;
    ret = read(file_size_fb, &client_file_size, sizeof(client_file_size));
    if (ret == 0) {
        lseek(file_size_fb, 0, SEEK_SET);
        write(file_size_fb, &client_file_size, sizeof(client_file_size));
    }

    off_t fileSize;
    ret = recv(sockfd, &train.length, sizeof(train.length), MSG_WAITALL);
    if (ret <= 0) {
        printf("接收文件大小长度失败");
        close(file_fd);
        close(file_size_fb);
        return -1;
    }

    ret = recv(sockfd, &fileSize, train.length, MSG_WAITALL);
    if (ret <= 0) {
        printf("接收文件大小失败");
        close(file_fd);
        close(file_size_fb);
        return -1;
    }
    ret = send(sockfd, &client_file_size, sizeof(client_file_size), MSG_NOSIGNAL);
    if (ret <= 0) {
        printf("发送客户端文件大小失败");
        close(file_fd);
        close(file_size_fb);
        return -1;
    }

    // 检查文件大小并预分配
    struct stat statbuf;
    if (fstat(file_fd, &statbuf) == -1) {
        printf("fstat 失败");
        close(file_fd);
        close(file_size_fb);
        return -1;
    }
    if (statbuf.st_size < fileSize) {
        if (ftruncate(file_fd, fileSize) == -1) {
            printf("ftruncate 失败");
            close(file_fd);
            close(file_size_fb);
            return -1;
        }
    }

    bzero(&train, sizeof(train));
    if (fileSize < 100 * 1024 * 1024) {
        printf("小于100M\n");
        char buffer[1024];
        while (client_file_size < fileSize) {
            size_t to_read = fileSize - client_file_size < sizeof(buffer) ? fileSize - client_file_size : sizeof(buffer);
            printf("%5.2lf%%\r", 100.0 * client_file_size / fileSize);
            fflush(stdout);
            ret = recv(sockfd, buffer, to_read, 0);
            if (ret <= 0) {
                printf("接收小文件数据失败");
                close(file_fd);
                close(file_size_fb);
                return -1;
            }
            write(file_fd, buffer, ret);
            client_file_size += ret;
            lseek(file_size_fb, 0, SEEK_SET);
            write(file_size_fb, &client_file_size, sizeof(client_file_size));
        }
        printf("100.00%%\n");
        if (client_file_size == fileSize) {
            printf("接收完毕\n");
            unlink(size_file_name);
        } else {
            printf("接收失败：文件大小不匹配 (%ld/%ld)\n", client_file_size, fileSize);
            close(file_fd);
            close(file_size_fb);
            return -1;
        }
    } else {
        printf("不小于100M\n");

        long page_size = sysconf(_SC_PAGESIZE);

        ssize_t lastSize = 0;
        off_t offset = client_file_size;
        const off_t CHUNK_SIZE = 1024 * 1024;

        while (client_file_size < fileSize) {
            size_t remaining = fileSize - client_file_size;
            size_t current_chunk_size = remaining < CHUNK_SIZE ? remaining : CHUNK_SIZE;

            off_t aligned_offset = (offset / page_size) * page_size;
            size_t ptr_offset = offset - aligned_offset;
            size_t mmap_length = current_chunk_size + ptr_offset;

            char *mapped = mmap(NULL, mmap_length, PROT_READ | PROT_WRITE, MAP_SHARED, file_fd, aligned_offset);
            if (mapped == MAP_FAILED) {
                printf("mmap 失败");
                close(file_fd);
                close(file_size_fb);
                return -1;
            }

            char *effective_ptr = mapped + ptr_offset;

            size_t received = 0;
            while (received < current_chunk_size) {
                ret = recv(sockfd, effective_ptr + received, current_chunk_size - received, 0);
                if (ret <= 0) {
                    printf("接收大文件失败");
                    munmap(mapped, mmap_length);
                    close(file_fd);
                    close(file_size_fb);
                    return -1;
                }
                received += ret;
            }

            if (munmap(mapped, mmap_length) == -1) {
                printf("munmap 失败");
                close(file_fd);
                close(file_size_fb);
                return -1;
            }

            client_file_size += received;
            offset += received;

            if (client_file_size - lastSize >= 10000000) {
                printf("\r%5.2lf%%", 100.0 * client_file_size / fileSize);
                lastSize = client_file_size;
            }
            lseek(file_size_fb, 0, SEEK_SET);
            write(file_size_fb, &client_file_size, sizeof(client_file_size));
        }

        if (client_file_size == fileSize) {
            printf("\r100.00%%\n"); 
            printf("接收完毕\n");
            unlink(size_file_name);
        } else {
            printf("接收失败：文件大小不匹配 (%ld/%ld)\n", client_file_size, fileSize);
            close(file_fd);
            close(file_size_fb);
            return -1;
        }
    }

    close(file_fd);
    close(file_size_fb);
    return 0;
}
int sendFileToServer(int netfb,char *fileName){
    int file_fb=open(fileName,O_RDWR);//这个文件名是命令里切出来的
    // ERROR_CHECK(file_fb,-1,"open");
    if(file_fb == -1)
    {
        perror(NULL);
        return -1;
    }
    train_t train;
    //  train.length=strlen(fileName);
    //  memcpy(train.data,fileName,strlen(fileName));
    //  send(netfb,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    //  send(netfb,train.data,train.length,MSG_NOSIGNAL);//客户端不发文件名
    struct stat statbuf;
    fstat(file_fb,&statbuf);
    off_t filesize=statbuf.st_size;
    train.length=sizeof(filesize);
    memcpy(train.value,&filesize,sizeof(filesize));
    send(netfb,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    send(netfb,train.value,train.length,MSG_NOSIGNAL);//发文件大小
    off_t current_send_over_length=-1;
    recv(netfb,&current_send_over_length,sizeof(current_send_over_length),MSG_WAITALL);
    int lseek_ret=lseek(file_fb,current_send_over_length,SEEK_SET);//设置文件指向
    /* ERROR_CHECK(lseek_ret,-1,"lseek"); */
    if(filesize<100*1024*1024){
        printf("小于100M\n");
        while(1){//发文件内容
            char read_data[1024]={0};
            int ret=read(file_fb,read_data,sizeof(read_data));//这里不用小火车，直接全部发送
            train.length = ret;
            if(ret==0){
                send(netfb,&train.length,sizeof(train.length),MSG_NOSIGNAL);
                printf("上传完成\n");
                break;
            }
            send(netfb,&train.length,sizeof(train.length),MSG_NOSIGNAL);
            send(netfb,read_data,train.length,MSG_NOSIGNAL);
        }
    }else{
        off_t offset = current_send_over_length;
        off_t CHUNK_SIZE = 1024 * 1024; // 1MB，每次发送<=1MB的数据
        long page_size = sysconf(_SC_PAGESIZE); // 获取系统页大小（通常 4KB）

        while (offset < filesize) {
            size_t remaining = filesize - offset;
            size_t current_chunk_size = remaining < CHUNK_SIZE ? remaining : CHUNK_SIZE;

            // 计算对齐后的 offset（向下取整到页边界）
            off_t aligned_offset = (offset / page_size) * page_size;
            // 计算映射后指针需要偏移的量（offset - aligned_offset）,
            size_t ptr_offset = offset - aligned_offset;//ptr_offset > 0
            // 实际映射的长度 = 当前块大小 + 额外对齐偏移（避免越界）
            size_t mmap_length = current_chunk_size + ptr_offset;

            // 映射对齐后的区域
            char *mapped = mmap(NULL, mmap_length, PROT_READ, MAP_SHARED, file_fb, aligned_offset);
            if (mapped == MAP_FAILED) {
                perror("mmap failed");
                close(file_fb);
                return -1;
            }

            // 调整指针，使其指向文件要开始发送的位置
            char *effective_ptr = mapped + ptr_offset;
            // 实际发送的数据大小仍然是 current_chunk_size
            size_t sent = 0;
            while (sent < current_chunk_size) {
                ssize_t bytes_sent = send(netfb, effective_ptr + sent, current_chunk_size - sent, MSG_NOSIGNAL);
                if (bytes_sent == -1) {
                    perror("send failed");
                    munmap(mapped, mmap_length);
                    close(file_fb);
                    return -1;
                }
                sent += bytes_sent;
            }

            // 清理映射
            munmap(mapped, mmap_length);
            offset += current_chunk_size;
        }
        printf("上传完成\n");
        close(file_fb);
    }
    return 0;
}

